////////////////////////////////////////////////////////////////////////////////////////////////////

#include "CMayaCameraAnimator.h"
#include "CLevelEditor.h"
#include "CEventReceiver.h"
#include "ISceneManager.h"
#include "ISceneNode.h"
#include "ICameraSceneNode.h"
#include "ICursorControl.h"
#include "Commons.h"
////////////////////////////////////////////////////////////////////////////////////////////////////

extern CLevelEditor* editor;

////////////////////////////////////////////////////////////////////////////////////////////////////

//! constructor
CSceneNodeAnimatorCameraMaya::CSceneNodeAnimatorCameraMaya(gui::ICursorControl* cursor, f32 rotate, f32 zoom, f32 translate)
 :	CursorControl(cursor), Zooming(false), Rotating(false), Moving(false), Translating(false),
	ZoomSpeed(zoom), RotateSpeed(rotate), TranslateSpeed(translate),
	RotateStartX(0.0f), RotateStartY(0.0f), ZoomStartX(0.0f), ZoomStartY(0.0f),
	TranslateStartX(0.0f), TranslateStartY(0.0f), CurrentZoom(70.0f), RotX(0.0f), RotY(0.0f), 
	Target(0,0,0), OldTarget(0,0,0), OldCamera(0), MousePos(0.5f, 0.5f)
{
	#ifdef _DEBUG
	setDebugName("CSceneNodeAnimatorCameraMaya");
	#endif

	if (CursorControl)
	{
		CursorControl->grab();
		MousePos = CursorControl->getRelativePosition();
	}

	allKeysUp();

	m_bWheel = false;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//! destructor
CSceneNodeAnimatorCameraMaya::~CSceneNodeAnimatorCameraMaya()
{
	if (CursorControl)
		CursorControl->drop();
}

////////////////////////////////////////////////////////////////////////////////////////////////////

//! It is possible to send mouse and key events to the camera. Most cameras
//! may ignore this input, but camera scene nodes which are created for 
//! example with scene::ISceneManager::addMayaCameraSceneNode or
//! scene::ISceneManager::addMeshViewerCameraSceneNode, may want to get this input
//! for changing their position, look at target or whatever.
bool CSceneNodeAnimatorCameraMaya::OnEvent(const SEvent& event)
{
	if (event.EventType != EET_MOUSE_INPUT_EVENT)
		return false;

	switch(event.MouseInput.Event)
	{
	case EMIE_LMOUSE_PRESSED_DOWN:
		MouseKeys[0] = true;
		break;
	case EMIE_RMOUSE_PRESSED_DOWN:
		MouseKeys[2] = true;
		break;
	case EMIE_MMOUSE_PRESSED_DOWN:
		MouseKeys[1] = true;
		break;
	case EMIE_LMOUSE_LEFT_UP:
		MouseKeys[0] = false;
		break;
	case EMIE_RMOUSE_LEFT_UP:
		MouseKeys[2] = false;
		break;
	case EMIE_MMOUSE_LEFT_UP:
		MouseKeys[1] = false;
		break;
	case EMIE_MOUSE_MOVED:
		MousePos = CursorControl->getRelativePosition();
		break;
	case EMIE_MOUSE_WHEEL:
		m_bWheel = true;
		m_wheel = event.MouseInput.Wheel;
		break;
	case EMIE_COUNT:
		return false;
	}
	return true;
}


//! OnAnimate() is called just before rendering the whole scene.
//! nodes may calculate or store animations here, and may do other useful things,
//! dependent on what they are.
void CSceneNodeAnimatorCameraMaya::animateNode(ISceneNode *node, u32 timeMs)
{
	if (node->getType() != ESNT_CAMERA)
		return;

	ICameraSceneNode* camera = static_cast<ICameraSceneNode*>(node);

	vector3df target = camera->getTarget();
	vector3df pos = camera->getPosition();
	vector3df up = camera->getUpVector();
	up.normalize();
	vector3df lookAt = target - pos;
	lookAt.normalize();
	vector3df right = up.crossProduct(lookAt);
	right.Z = 0;
	right.normalize();

	if (isMouseKeyDown(0))
	{
		// rotate
		if (Rotating)
		{
			float rotZ = 0;
			if (StartMousePos.X - MousePos.X != 0)
			{
				rotZ = RotateSpeed / 10 * (StartMousePos.X - MousePos.X);
			}

			float rotX = 0;
			if (StartMousePos.Y - MousePos.Y != 0)
			{
				rotX = RotateSpeed / 10 * (StartMousePos.Y - MousePos.Y);
			}

			quaternion qZ; qZ.fromAngleAxis(rotZ, up);
			quaternion qX; qX.fromAngleAxis(rotX, right);

			quaternion q = qX * qZ;

			ISceneNode* pNode = editor->getSelectedNode();
			if (pNode && g->eventReceiver->IsKeyDown(KEY_SHIFT))
			{
				target = pNode->getAbsolutePosition();
				
				f32 dist = lookAt.dotProduct(pos) - lookAt.dotProduct(target);
				if (dist > 0) right = -right;

				vector3df toPos = pos - target;
				toPos = q * toPos;
				camera->setPosition(target + toPos);
				camera->setTarget(target);

				vector3df newRight = q * right;
				newRight.Z = 0;
				vector3df newUp = -toPos.crossProduct(right);
				camera->setUpVector(newUp);
			}
			else
			{
				vector3df toPos = target - pos;
				toPos = q * toPos;
				camera->setTarget(pos + toPos);
				vector3df newRight = q * right;
				newRight.Z = 0;
				vector3df newUp = toPos.crossProduct(right);
				camera->setUpVector(newUp);
			}

			StartMousePos = MousePos;
		}
		else
		{
			Rotating = true;
			StartMousePos = MousePos;
		}
	}
	else
	{
		Rotating = false;
	}

	if (isMouseKeyDown(1))
	{
		// zoom
		if (Zooming)
		{
			float zoomY = ZoomSpeed * (StartMousePos.Y - MousePos.Y);

			vector3df toPos = target - pos;
			toPos.normalize();

			pos += toPos * zoomY;
			target += toPos * zoomY;
			camera->setPosition(pos);
			camera->setTarget(target);

			StartMousePos = MousePos;
		}
		else
		{
			Zooming = true;
			StartMousePos = MousePos;
		}
	}
	else
	{
		Zooming = false;
	}

	if (isMouseKeyDown(2))
	{
		// translate
		if (Translating)
		{
			float camDis = pos.getDistanceFrom(irr::core::vector3df(0,0,0));
			float transFactor = camDis/1420;
			//DEBUG_OUT("transFactor=%f\n",transFactor);
			if(transFactor<1.0f)
				transFactor = 1.0f;
			else if(transFactor>10.0f)
				transFactor = 10.0f;

			float transX = TranslateSpeed * (StartMousePos.X - MousePos.X) * transFactor;
			float transY = TranslateSpeed * (MousePos.Y - StartMousePos.Y) * transFactor;			
			
			target += transX * right + transY * up;
			pos += transX * right + transY * up;
			camera->setPosition(pos);
			camera->setTarget(target);

			StartMousePos = MousePos;
		}
		else
		{
			Translating = true;
			StartMousePos = MousePos;
		}
	}
	else
	{
		Translating = false;
	}

	if (m_bWheel)
	{
		float zoomY = m_wheel * 5;

		vector3df toPos = target - pos;
		toPos.normalize();

		pos += toPos * zoomY;
		target += toPos * zoomY;
		camera->setPosition(pos);
		camera->setTarget(target);

		m_bWheel = false;
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////

bool CSceneNodeAnimatorCameraMaya::isMouseKeyDown(s32 key)
{
	return MouseKeys[key];
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void CSceneNodeAnimatorCameraMaya::allKeysUp()
{
	for (s32 i=0; i<3; ++i)
		MouseKeys[i] = false;
}

////////////////////////////////////////////////////////////////////////////////////////////////////

// function added by jox
void CSceneNodeAnimatorCameraMaya::updateAnimationState() 
{
	core::vector3df pos(Pos - Target);

	// X rotation
	core::vector2df vec2d(pos.X, pos.Y);
	RotX = (f32)vec2d.getAngle();

	// Y rotation
	pos.rotateXYBy(RotX, core::vector3df());
	vec2d.set(pos.X, pos.Z);
	RotY = -(f32)vec2d.getAngle();

	// Zoom
	CurrentZoom = (f32)Pos.getDistanceFrom(Target);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
//! Sets the rotation speed
void CSceneNodeAnimatorCameraMaya::setRotateSpeed(f32 speed)
{
	RotateSpeed = speed;	
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
//! Sets the movement speed
void CSceneNodeAnimatorCameraMaya::setMoveSpeed(f32 speed)
{
	TranslateSpeed = speed;
}
//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------

//! Sets the zoom speed
void CSceneNodeAnimatorCameraMaya::setZoomSpeed(f32 speed)
{
	ZoomSpeed = speed;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
//! Gets the rotation speed
f32 CSceneNodeAnimatorCameraMaya::getRotateSpeed() const
{
	return RotateSpeed;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
// Gets the movement speed
f32 CSceneNodeAnimatorCameraMaya::getMoveSpeed() const
{
	return TranslateSpeed;
}
//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
//! Gets the zoom speed
f32 CSceneNodeAnimatorCameraMaya::getZoomSpeed() const
{
	return ZoomSpeed;
}
//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
ISceneNodeAnimator* CSceneNodeAnimatorCameraMaya::createClone(ISceneNode* node,	ISceneManager* newManager)
//ISceneNodeAnimator* CSceneNodeAnimatorCameraMaya::createClone()
{
	CSceneNodeAnimatorCameraMaya * newAnimator = 
		new CSceneNodeAnimatorCameraMaya(CursorControl, RotateSpeed, ZoomSpeed, TranslateSpeed);
	return newAnimator;
}
//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------